package com.qingzhu.component.lock.common.entity;

import com.qingzhu.component.lock.common.constant.AutoConfig;
import com.qingzhu.component.lock.common.exception.LockExpiredException;
import com.qingzhu.component.lock.common.pool.ForkLockExecutor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

/**
 * 抽象的顶层锁实体类
 * 封装了调用的关键方法
 * @author xiangjz
 * @version 1.0
 * @date 2021/1/14 16:59
 */
@Data
@Slf4j
public abstract class LockEntity<K> {

    /**
     * 锁的业务key
     */
    private volatile K lockKey;

    /**
     * 当前独占线程
     */
    private volatile Thread currentExclusiveThread;

    /**
     * 锁过期时间 默认永不过期
     */
    private volatile long expireTime;

    /**
     * 整个进程内的，当fork的线程数达到阈值后，等待的时长
     */
    private volatile long expireOnForkingFull;

    /**
     * 整个进程内的，当fork的线程数限制器
     */
    private ForkLockExecutor forkLockExecutor;

    /**
     * 锁的过期策略类型
     */
    private volatile int strategyOnExpired;

    /**
     * 业务执行过程中抛的异常(这个参数会在lock之初设置为null，在catch到异常时设置为当前业务抛的异常)
     * 为了解决aop中future执行joinPoint.proceed方法默认需要catch异常，会与FutureTask中的run方法catch异常冲突，捕获不到
     */
    private Throwable t;

    protected LockEntity(K lockKey, long expireTime) {
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        setCurrentThread();
    }

    public final void setCurrentThread(Thread thread) {
        this.currentExclusiveThread = thread;
    }

    public final void setCurrentThread() {
        setCurrentThread(Thread.currentThread());
    }

    /**
     * 加锁
     */
    public final void lock() {
        this.doLock();
        // 加锁完毕之后，需要尝试申请一下进程内配置的fork线程数量资源
        try{
            this.forkLockExecutor.awaitIfFull(this.expireOnForkingFull);
        } catch (LockExpiredException e) {
            log.error("{} business of lock key '{}' execute failed, cause of {}", AutoConfig.COMPONENT_LOCK_LOG_PREFIX, this.lockKey, e);
            this.currentExclusiveThread = null;
            this.doUnlock();
            throw e;
        }
    }

    public abstract boolean doLock();

    /**
     * 释放锁
     */
    public final void unlock() {
        this.currentExclusiveThread = null;
        this.doUnlock();
        // 释放进程内的fork线程数资源
        this.forkLockExecutor.release();
    }

    public abstract void doUnlock();

}
